#!/bin/bash
# Autor:             Saúl Martínez Vidals
# Descripción:       Realiza las tareas necesarias para que cada equipo de los LDCCBI fun-
#                    cione correctamente.
# Fecha de creación: 28 de Octubre, 2008

shopt -s -o nounset

# comandos y "scripts" necesarios
declare -ax dependencias

# variables globales
declare -rx SCRIPT=${0##*/}                 # nombre del "script"
declare GATEWAY="251"                       # número de host que funciona como Gateway
declare DNS="148.206.32.29"                 # DNS por defecto
declare DOMINIO="192.168.1"                 # dominio por defecto de los equipos
declare NOMBRE="ichigo"                     # nombre de la máquina
declare manual="man.sh"						# script para mostrar el manual de usuario
declare m_confirmacion                      # mensaje de confirmación
declare -i tarea_manual                     # indica si el usuario quiere ver el manual

# arreglos globales
declare -a  opts                            # nombre de las opciones
declare -a  args                            # texto para los argumentos de la opción
declare -a  obs                             # indica si la opción es obligatoria
declare -a  descs                           # descripción de cada opción
declare -ia nargs                           # número de argumentos de cada opción
declare -a  confs                           # mensajes de confirmación
declare -a  tareas                          # tareas que se ejecutarán


# carga las dependencias
function config_dependencias
{
    dependencias[0]="$manual"               # herramienta para el formateo de manuales
    dependencias[1]="/usr/bin/seq"          # generación se números secuaneciales
    dependencias[2]="/sbin/ifconfig"        # se usa para la configuración de la inter-
                                            # faz de red
    dependencias[3]="/bin/cat"
}


# aquí se declaran las opciones que queremos que soporte el programa, es en esta función
# donde se especifican nuevas funcionalides
function config_opciones
{
    declare -i i=-1
    
    # manual de usuario
    i=$i+1
    opts[$i]="h"
    descs[$i]="manual de usuario"
    
    # dominio de red
    i=$i+1
    opts[$i]="dominio"
    nargs[$i]=1
    args[$i]="<x.y.z>"
    descs[$i]="configura el dominio de red. El dominio por defecto es 192.168.1"
    confs[$i]="Cambiar el dominio a \$1"
    
    # gateway
    i=$i+1
    opts[$i]="gtw"
    nargs[$i]=1
    args[$i]="<n>"
    descs[$i]+="Cambia la dirección del host que funge como gateway. Si "
    descs[$i]+="no se usa esta opción la dirección por defecto es \$DOMINIO.\$GATEWAY"
    confs[$i]="Cambiar la dirección del gateway de \$DOMINIO.\$GATEWAY a \$DOMINIO.\$1"
    
    # interfáz de red
    i=$i+1
    opts[$i]="red"
    nargs[$i]=2
    args[$i]="<i> <n>"
    descs[$i]+="configura una interfaz de red <i>. Asigna al equipo la dirección IP "
    descs[$i]+="<DOMINIO>.<n> donde <DOMINIO> es 192.168.1 u otro especificado con la "
    descs[$i]+="bandera -dominio"
    confs[$i]="Configurar la interfáz de red \$1 con la dirección IP \<DOMINIO\>.\$2"

    # apt-cacher
    i=$i+1
    opts[$i]="apt-cacher"
    nargs[$i]=1
    args[$i]="<n|s>"
    descs[$i]+="configura al equipo para usar apt-cacher, 'n' es el número de la máquina"
    descs[$i]+=" que funge como servidor de paquetes, si en lugar de un número, se espec"
    descs[$i]+="ifica el carácter 's', se configura la máquina como servidor."
    confs[$i]="Configurar \$(if [ \"\$1\" = \"s\" ]; then echo servidor apt-cacher; else"
    confs[$i]+=" echo cliente apt-cacher, el servidor será el equipo \$DOMINIO.\$1; fi)"
}


# manual de usuario
function config_manual
{
    # variable con las opciones del manual
    declare uso
    
    # descripción corta
    desc="configuración de software y otras acciones en un equipo"
    
    # descripción larga del script
    descripcion+="Realiza las tareas necesarias para que cada equipo de los LDCCBI func"
    descripcion+="ione correctamente. Cada tarea se ejecuta secuencialmente, es decir, "
    descripcion+="la opción que especifique primero será la primera en realizarse."
    
    # ejemplo de uso
    ejemplo="bash $SCRIPT -dominio 192.168.1 -gtw 251 -red \"eth0\" 1"
    
    # toma la información del manual de los arreglos globales
    for i in $(seq 0 $((${#opts[@]}-1))); do
        uso+="-o \"${opts[$i]}\" \"${args[$i]}\" \"${obs[$i]}\" \"${descs[$i]}\" "
    done
    
    # muestra el manual en la pantalla
    eval "./$manual -s $SCRIPT -n \"$desc\" -d \"$descripcion\" -e \"$ejemplo\" $uso"
}


# verifica que las herramientas necesarias estén instaldas
function verifica_dependencias
{
    # badera para saber si ocurrieron errores
    declare -i errores=0

    # carga en memoria el arreglo que contiene las rutas de las herramientas necesarias
    config_dependencias
    
    # verifica una por una cada dependencia
    for i in "${dependencias[@]}"; do
        if [ ! -x "$i" ]; then
            echo -e "\e[1;31mEl archivo \e[1;37m$i \e[1;31mno existe o no tiene" \
                    " permisos de ejecución\e[0;m\n";
            errores=1
        fi
    done
    
    # si ocurrió algún error termina la ejecución del programa
    if [ $errores -eq 1 ]; then exit 1; fi
}


# realiza algunos ajustes en los valores de los arreglos globles
function config_ajustes
{
    # si no se ha definido la descripción o los argumentos para alguna opción, se
    # asignan valores por defecto
    for i in $(seq 0 $((${#opts[@]}-1))); do
        # el valor por defecto de los argumentos para cada opción es una cadena vacía
        args[$i]=${args[$i]:=""}
        
        # el valor por defecto de la descripción para cada opción es una cadena vacía
        descs[$i]=${descs[$i]:=""}
        
        # el valor por defecto del número de argumentos es 0
        nargs[$i]=${nargs[$i]:=0}
        
        # por defecto, cada bandera (opción) es opcional
        obs[$i]=${obs[$i]:=0}
    done
}


# verifica los parámetros recibidos
function verifica_argumentos
{
    declare -i op_valida                    # bandera para saber si una opción es válida
    declare -i errores=0                    # bandera para saber si ocurrieron errores

	# si no se encuentran argumentos, muestra el manual y termina el programa
	if [ $# -eq 0 ]; then
		echo -e "\e[1;31mFaltan argumentos\e[0m\n"
        config_manual
		exit 1
	fi

	# analiza cada uno de los parámetros
	while [ $# -gt 0 ] ; do
	    op_valida=0

	    # busca la opción en el arreglo global $opts
		for i in $(seq 0 $((${#opts[@]}-1))); do
		    if [ "-${opts[$i]}" = "$1" ]; then
		        op_valida=1
		        
		        # verifica si se trata de la bandera -h para mostrar el manual, cuando 
		        # no se trata del manual, verifica que exista la función que implemente
		        # las acciones a realizar
		        if [ "-h" = "$1" ]; then
		            tarea_manual=1
                else
                    declare -F tarea_${opts[$i]} > /dev/null
                    
                    if (($?)); then
                        echo -e "\e[1;31mLa función \e[1;37mtarea_${opts[$i]} \e[1;31m"\
                                "no está implementada\e[0;m";
		                errores=1
                    fi
                fi
		        break
		    fi
		done
		
		# verifica si se encontró la opción
		if [ $op_valida -eq 0 ]; then
		    echo -e "\e[1;31mLa opción \e[1;37m$1 \e[1;31mno está soportada\e[0;m";
		    errores=1
		    shift
        else
            shift
            tmp="tarea_${opts[$i]}"
            
            m_confirmacion+=" - $(eval echo -e ${confs[$i]:=""})\n"
            for j in $(seq ${nargs[$i]}); do
                tmp+=" $1"
                shift
            done
            tareas[${#tareas[@]}]="$tmp"
		fi
	done
	
	# si la bandera del manual está activa se muestra
    if [ "$tarea_manual" = "1" ]; then echo; config_manual; exit 1; fi

	# si ocurrió algún error termina la ejecución del programa
    if [ $errores -eq 1 ]; then exit 1; fi
}


# mensaje de confirmación
function config_confirmacion
{
    # variable que contiene la respuesta del usuario
    declare r
    
    # mensaje de confirmación
    echo -e "\e[1;37mSe realizaran las siguientes tareas:\e[0m\n\n$m_confirmacion"
    
    # lee la respuesta del usuario
    while [ "$r" != "s" -a "$r" != "S" -a "$r" != "N" -a "$r" != "n" ]; do
        echo -en "\e[1;37m¿Desea continuar [s/n]?\e[0m "
        read r
    done
    
    # si la respuesta en afirmativa se ejecutan las tareas pendientes
    if [ $r = "s" -o $r = "S" ]; then
        echo
        for i in $(seq 0 $((${#tareas[@]}-1))); do
            eval "${tareas[$i]}"
            echo -e "\n"
        done
    else
        exit 0
    fi
}


# función principal
function main
{
    verifica_dependencias
    config_opciones
    config_ajustes
    verifica_argumentos "$@"
    config_confirmacion
}


# función que implementa la opción -dominio
function tarea_dominio
{
    echo -e "\e[1;32mCambiando el dominio de red a $1\e[0m"
    DOMINIO="$1"
    echo "dominio de red actualizado a $DOMINIO"
}


# función que implementa la opción -red
function tarea_red
{
    declare RED
    declare HOSTS
    
    echo -e "\e[1;32mConfigurando la interfaz de red '$1' ...\e[0m"
    
    ifconfig "$1" 2>/dev/null > /dev/null
    
    # verifica que exista la interfaz de red especificada
    if (($?)); then
        echo -e "\e[1;31m$1: Dispositivo no encontrado\e[0m"
        return
    fi

    # verifica que el número esté entre 1 y 254
    # si el número es menor o igual que 0 ó mayor o igual que 255 termina
    if [ $2 -le 0 -o $2 -ge 255 ]; then
        echo -e "\e[1;31m$1: número de host incorrecto, " \
                "el valor debe estar en el rango [1-254]\e[0m"
        return
    fi

	# configura la interfaz se red editando el archivo /etc/network/interfaces y muestra
	# un mensaje de confirmación o error
    RED+="auto lo\n"
    RED+="iface lo inet loopback\n\n"
    RED+="auto $1\n"
    RED+="iface $1 inet static\n"
    RED+="address $DOMINIO.$2\n"
    RED+="netmask 255.255.255.0\n"
    RED+="gateway $DOMINIO.$GATEWAY\n"
    echo -e "$RED"  > /etc/network/interfaces
    
    if (($?)); then
    	echo -e "\e[1;31mInterfaz de red no configurada\e[0m"
    else
    	echo interfaz de red configurada correctamente
    fi

	# configura el DNS y muestra un mensaje de configuración o error
    echo "nameserver $DNS" > /etc/resolv.conf
    if (($?)); then
    	echo -e "\e[1;31mError al intentar escribir en el archivo /etc/resolv.conf\e[0m"
    else
    	echo DNS configurado correctamente
    fi
}


# función que implementa la opción -gtw
function tarea_gtw
{
    echo -e "\e[1;32mCambiando el dominio de red\e[0m"
    # verifica que el número esté entre 1 y 254
    # si el número es menor o igual que 0 ó mayor o igual que 255 termina
    if [ $1 -le 0 -o $1 -ge 255 ]; then
        echo -e "\e[1;31m$1: número de host incorrecto, " \
                "el valor debe estar en el rango [1-254]\e[0m"
        return
    fi
    
    GATEWAY="$1"
    echo "dirección del gateway actualizada a $DOMINIO.$GATEWAY"
}


# función que implementa la opción -red
function tarea_apt-cacher
{
    # servidor
    if [ "$1" = "s" -o "$1" = "S" ]; then
        echo -e "\e[1;32mConfigurando servidor apt-cacher\e[0m"
        echo "servidor apt-cacher configurado correctamente"
    # cliente
    elif [ $1 -eq $1 2> /dev/null ]; then
        echo -e "\e[1;32mConfigurando cliente apt-cacher\e[0m"
        
        # verifica que el número esté entre 1 y 254
        # si el número es menor o igual que 0 ó mayor o igual que 255 termina
        if [ $1 -le 0 -o $1 -ge 255 ]; then
            echo -e "\e[1;31m$1: número de host incorrecto, " \
                    "el valor debe estar en el rango [1-254]\e[0m"
            return
        fi
        
        # cuando llega a este punto, el número es correcto
        echo "cliente apt-cacher configurado correctamente"

    # valor incorrecto
    else
        echo -e "\e[1;31m$1 no es un número entero\e[0m"
    fi
}


# llama a la función principal
reset
main "$@"

exit 0
